####  QUICK 'N DIRTY GAME LIBRARY  ####
#--------------------------------#
#These are a bunch of things I use in almost all games I make.
#You can either import this module, or copy/paste the functions you want
#to your game so they are easier to edit and can make use of global
#variables for convenience
#
#PS: This is released under the MIT license

import pygame, os
from pygame.locals import *

BASEPATH = os.path.abspath(__file__)
BASEPATH = BASEPATH[:BASEPATH.index('src')]
print BASEPATH

####CLASSES####
class Empty:
	"""	An empty class that can be handy for storing data."""
	def __init__(self, **args):
		pass
class InputHandler:
	"""
	Handles keyboard and mouse states. Has lists  keys and mousebuttons
	and variables mousepos, mousex and mousey."""
	def __init__(self):
		self.keys = []
		for i in range(512):
			self.keys.append(False)
		self.mousepos = self.mousex, self.mousey = 0,0
		self.mousebuttons = [False,False,False,False,False,False,False]
	def update(self, updateMouse = True):
		"""Call this to update key and mouse states."""
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				exit()
			elif event.type == pygame.KEYDOWN:
				if event.key == K_ESCAPE:
					exit()
				self.keys[event.key] = True
				self.key_pressed(event.key)
				continue
			elif event.type == pygame.KEYUP:
				self.keys[event.key] = False
				continue
			elif event.type == pygame.MOUSEBUTTONDOWN:
				self.mousebuttons[event.button] = True
				self.mouse_pressed(event.button)
				continue
			elif event.type == pygame.MOUSEBUTTONUP:
				self.mousebuttons[event.button] = False
				continue
		self.mousepos = self.mousex,self.mousey = pygame.mouse.get_pos()
	def key_pressed(self,key):
		"""
		Called when a key is pressed. Use this if you make a class
		extending InputHandler.
		"""
		pass
	def mouse_pressed(self,button):
		"""
		Called when a mouse button is pressed. Use this if you make a 
		class extending InputHandler.
		"""
		pass
class Color:
	"""Stores rgb tuples with names"""
	BLACK = (0,0,0)
	WHITE = (255,255,255)
	RED = (255,0,0)
	GREEN = (0,255,0)
	BLUE = (0,0,255)
	YELLOW = (255,255,0)
	CYAN = (0,255,255)
	PURPLE = (255,0,255)
class Camera:
	"""
	The camera requires a surface to be attatched to; most likely you
	want this to be your screen. Optionally, the maximum coordiates
	can be passed so the camera does no scroll off the edge of the
	screen.
	"""
	def __init__(self,screen,x=0,y=0,maxx=0,maxy=0,z=1):
		self.screen = screen
		self.x = x
		self.y = y
		self.z = z
		self.maxx = maxx-1
		self.maxy = maxy-1
		self.screenWidth = screen.get_width()
		self.screenHeight = screen.get_height()
		#~ print "max:", self.maxx, self.maxy
	def update(self, screen, fieldSize = None):
		self.screen = screen
		self.screenWidth = screen.get_width()
		self.screenHeight = screen.get_height()
		if fieldSize != None:
			self.maxx = fieldSize[0]-1
			self.maxy = fieldSize[1]-1
	def move(self, x=None,y=None, relative=False, z=None):
		"""
		Moves the camera to the given coordinate. Does not require
		both x and y.
		"""
		if relative:
			if x!= None:
				x = self.x + x
			if y!= None:
				y = self.y + y
			if z!= None:
				z = self.z + z
		if x!=None:
			if x > 0:
				if x+self.screenWidth>self.maxx:
					self.x = self.maxx-self.screenWidth
				else:
					self.x = x
			else:
				self.x = 0
		else:
			self.x = 0
		if y!=None:
			if y > 0:
				if y+self.screenHeight>self.maxy:
					self.y = self.maxy-self.screenHeight
				else:
					self.y = y
			else:
				self.y = 0
		else:
			self.y = 0
		if z!=None:
			self.z=z
	def center(self, (x,y)):
		"""Centers the camera on the given (x,y) coordinate."""
		if x-self.screenWidth/2 > 0:
			if x+self.screenWidth/2 > self.maxx:
				self.x = self.maxx-self.screenWidth
			else:
				self.x = x-self.screenWidth/2
		else:
			self.x = 0
		if y-self.screenHeight/2 > 0:
			if y+self.screenHeight/2 > self.maxy:
				self.y = self.maxy-self.screenHeight
			else:
				self.y = y-self.screenHeight/2
		else:
			self.y = 0
		self.x = int(self.x)
		self.y = int(self.y)
	def centerSmooth(self, (x,y), speed=2, moveMouse = None):
		center = self.x+self.screenWidth/2,self.y+self.screenHeight/2

		distance = math.hypot(self.x-x,self.y-y)

		dx = int((x - center[0])/speed)
		dy = int((y - center[1])/speed)

		if abs(dx) < 10:
			dx /= 2
		if abs(dy) < 10:
			dy /= 2

		newx = center[0] + dx #((self.x+self.screenWidth/2)*speed+x)/(speed+1)
		newy = center[1] + dy #((self.y+self.screenHeight/2)*speed+y)/(speed+1)

		self.center((newx,newy))
		if moveMouse != None:
			if self.x != 0 and self.y == 0:
				pygame.mouse.set_pos((moveMouse.screenx-dx,moveMouse.screeny))
			elif self.x == 0 and self.y != 0:
				pygame.mouse.set_pos((moveMouse.screenx,moveMouse.screeny-dy))
			if self.x != 0 and self.y != 0:
				pygame.mouse.set_pos((moveMouse.screenx-dx,moveMouse.screeny-dy))

####FUNCTIONS####
def draw_text(targetSurface, text, (x,y), font, color = (255,255,255), alpha=None):
	"""
	Draws text on the targetSurface at pos in font and color. Defaults 
	to white. Substitute "c" for a coordinate to center on that axis.
	Alpha support is in development. Currently, the font becomes aliased
	when alpha drawing is enabled for the font.
	 """
	text = str(text)
	if "c" not in (x,y):
		textSurface = font.render(text, True, color)
		targetSurface.blit(textSurface, (x,y))
		return
	else:
		width,height = font.size(text)
		surfaceWidth, surfaceHeight = targetSurface.get_size()
		if x == "c":
			x = surfaceWidth/2-width/2
		if y == "c":
			y = surfaceHeight/2-height/2
	if alpha == None:
		textSurface = font.render(text, True, color)
		targetSurface.blit(textSurface, (x,y))
		return
	font.render(text, False, color)
	textSurface.set_alpha(alpha)
	targetSurface.blit(textSurface, (x,y))
def load_image(name, folder = 'images', alpha = True):
	"""Load and convert an image. Returns the image handle."""
	if type(folder) == str:
		fullname = os.path.join(BASEPATH,folder, name)
	else:
		fullname = os.path.join(BASEPATH,folder[0], name)
		for directory in folder:
			name = os.path.join(directory, name)
		fullname = name
	try:
		image = pygame.image.load(fullname)
		if image.get_alpha() is None or not alpha:
			image = image.convert()
		else:
			image = image.convert_alpha()
	except pygame.error, message:
		print 'Cannot load image:', fullname
		raise SystemExit, message
	return image
def load_tileset(file,folder = 'images', tileWidth=32,tileHeight=32):
	"""
	Creates a tileset out of the given image. Requires load_image().
	"""
	image = load_image(file, folder)
	width = image.get_width()
	height = image.get_height()
	tileset = []
	for j in range(width/tileWidth):
		for i in range(height/tileHeight):
			tile = pygame.Surface((tileWidth,tileHeight))
			tile.blit(image,(0,0),(tileWidth*i,tileHeight*j,tileWidth,tileHeight))
			tileset.append(tile)
	return tileset
def load_sound(name, folder = 'sounds'):
	"""Load a sound and returns pygame Sound object handle"""
	if not pygame.mixer: #Test for sound capabilities
		print "pygame.mixer not functioning"
		exit()
	if type(folder) == str:
		fullname = os.path.join(BASEPATH, folder, name)
	else:
		for directory in folder:
			name = os.path.join(directory, name)
		fullname = name
	try:
		sound = pygame.mixer.Sound(fullname)
	except pygame.error:
		print ('Cannot load sound:', fullname)
		raise SystemExit, message
	return sound
def load_tilemap(file, folder = 'levels'):
	"""
	Loads and returns a tilemap from the given file in the given folder
	"""
	print "Loading Map", file
	file = open(os.path.join(BASEPATH,folder, file), "r")
	lines = file.readlines()
	file.close()

	if "," not in lines[0]:
		tilemap = [map(int,line.split()) for line in lines]
		return tilemap
	
	print "tilemap in CSV format, you should convert it"
	tilemap = []
	file.close()
	currentLine = 1
	totalLines = len(lines)
	for line in lines:
		row = line.replace("\n","").split(",")
		newRow = []
		for i in row:
			newRow.append(int(i))
		tilemap.append(newRow)

		if currentLine%32 == 0:
			print "Loaded" + str(currentLine)+"/"+str(totalLines)
		currentLine += 1
	print "Map Loaded"
	return tilemap
def save_tilemap(tilemap, file, folder = 'levels'):
	"""
	Saves the given tilemap to a the file in the given folder
	(defaults to maps)
	"""
	print "Saving Map..."
	filename = os.path.join(BASEPATH,folder, file)
	file = open(filename, "w")

	currentLine = 1
	totalLines = len(tilemap)

	for row in tilemap:
		newRow = []
		for tile in row:
			newRow.append(str(tile))
		file.write(" ".join(newRow)+"\n")

		if totalLines > 64 and currentLine%32 == 0:
			print "Saved "+str(currentLine)+"/"+str(totalLines)
		currentLine += 1
	file.close()
	print "Saved to: " + filename
def draw_tiles(targetSurface, camera, tilemap, tileset, tileWidth=32,tileHeight=32):
	"""
	Draws the background tiles using given tileset and tilemap relative 
	to the camera. Assumend tile size is 32x32, though can be set with the
	tileWidth and tileHeight parameters.
	"""
	windowWidth,windowHeight = targetSurface.get_size()
	startx = int(camera.x/tileWidth)
	endx = startx + int(windowWidth/tileWidth)+1
	starty = int(camera.y/tileHeight)
	endy = starty + int(windowHeight/tileHeight)+1
	for i in range(startx,endx):
		for j in range(starty,endy):
			try:
				targetSurface.blit(tileset[tilemap[i][j]], (i*tileWidth-camera.x,j*tileHeight-camera.y))
			except:
				pass
def draw_image(targetSurface, image, (x,y), angle = 0, scale = 1.0, smooth = False, camera = None, center = False, alpha = 255):
	"""
	Draws image on the targetSurface at position (x,y). Rotates the
	image by angle degrees counter-clockwise. Also scales image by a
	floating point muliplier that is scale. If smooth is on, it will use
	a smooth scaling filter. Keep in mind that scaling and rotating take
	up quite a bit of processing, and smooth scaling much more so. An
	optional camera object can be passed to specify drawing relative to
	that camera. Also, image can be drawn centered at the position if
	center is True.
	"""

	if "c" in (x,y):
		surfaceWidth, surfaceHeight = targetSurface.get_size()
		if x == "c":
			x = surfaceWidth/2
		if y == "c":
			y = surfaceHeight/2
	if angle == 0 and scale == 1.0:
		pass
	elif angle == 0:
		if not smooth:
			image = pygame.transform.scale(image, (int(image.get_width()*scale),int(image.get_height()*scale)))
		else:
			image = pygame.transform.smoothscale(image, (int(image.get_width()*scale),int(image.get_height()*scale)))
	elif scale == 1.0:
		image = pygame.transform.rotate(image, angle%360)
	else:
		if not smooth:
			image = pygame.transform.rotozoom(image, angle, scale)
		else:
			image = pygame.transform.rotate(pygame.transform.smoothscale(image, (int(image.get_width()*scale),int(image.get_height()*scale))),angle)
	if center:
		x -= image.get_width()/2
		y -= image.get_height()/2
	if camera != None:
		x = x-camera.x
		y = y-camera.y
	if alpha != 1:
		image.set_alpha(alpha)
	targetSurface.blit(image,(int(x),int(y)))
#debugging
if __name__ == '__main__':
	pygame.init()
	clock = pygame.time.Clock()
	screen = pygame.display.set_mode((500,500))
	input = InputHandler()
	camera = Camera(screen)
	mainfont = pygame.font.Font(None,22)
	sprite = load_image('kiwi.png')
	
	n=-360
	while True:
		n+=1
		input.update()
		screen.fill((100,100,100))
		draw_image(screen,sprite,('c','c'), angle = n, center= True, alpha = .4)
		
		for i,key in enumerate(input.keys):
			if key:
				draw_text(screen,str(i),((25*i)%400+50,(i/16)*26+50),mainfont,(255,255,255))
		pygame.display.flip()
		clock.tick(40)
	
